#include "Avalon/IOTests/MockClientConnection.hpp"
#include "Avalon/IO/ConnectException.hpp"
#include "Avalon/IO/PipedReader.hpp"
#include "Avalon/IO/PipedWriter.hpp"
#include "Avalon/IOTests/MockClientChannel.hpp"
#include "Avalon/IOTests/MockServerConnection.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::IO::Tests;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::signals2;

MockClientConnection::MockClientConnection(MockServerConnection* server,
    MockClientChannel* channel, const shared_ptr<PipedReader>& serverReader,
    const shared_ptr<PipedReader>& clientReader)
    : m_server(server),
      m_channel(channel),
      m_serverReader(serverReader),
      m_clientReader(clientReader),
      m_isConnected(false) {}

MockClientConnection::~MockClientConnection() {
  Close();
}

void MockClientConnection::Connect() {
  boost::unique_lock<boost::recursive_mutex> lock(m_mutex);
  if(m_isConnected) {
    return;
  }
  if(!m_server->AddPendingConnection(this)) {
    m_connectedCondition.wait(lock);
  }
  if(!m_isConnected) {
    BOOST_THROW_EXCEPTION(ConnectException("Connection rejected."));
  }
}

bool MockClientConnection::IsConnected() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  return m_isConnected;
}

void MockClientConnection::Close() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  if(!m_isConnected) {
    return;
  }
  m_server->RemoveConnection(this);
  CloseConnection();
}

connection MockClientConnection::ConnectClosedSignal(
    const ClosedSignal::slot_type& slot) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  return m_closedSignal.connect(slot);
}

connection MockClientConnection::ConnectClosedSignal(
    const ClosedSignal::slot_type& slot, bool* isConnected) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  *isConnected = m_isConnected;
  return m_closedSignal.connect(slot);
}

void MockClientConnection::AcceptConnection() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  m_isConnected = true;
  m_connectedCondition.notify_all();
}

void MockClientConnection::RejectConnection() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  m_isConnected = false;
  m_connectedCondition.notify_all();
}

void MockClientConnection::CloseConnection() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  if(!m_isConnected) {
    return;
  }
  m_serverReader->Break();
  m_clientReader->Break();
  m_isConnected = false;
  m_closedSignal();
}

Channel* MockClientConnection::CreateServerChannel() {
  return m_channel->CreateServerChannel();
}
